#include "race.h"
#include <boost/shared_ptr.hpp>

Race::Race( Map* mmap, vector<string>* m_logbufferQueue, const Timer& t, std::vector<boost::shared_ptr<Player> >& mplayers, boost::shared_ptr<btDynamicsWorld> ownerWorld ) : players(mplayers)
{
	m_ownerWorld = ownerWorld;
	map = mmap;
	logbufferQueue = m_logbufferQueue;

	ghostObject = 0;

	racestage = COUNTDOWN;
	countdownTime = 3;
	countdownCount = countdownTime+1;

	newLogMessage("prepare to race!");
	newMessageAllClients("ms prepare to race!;");

	resetTimer(t);

	// PLACE CARS ON THE STARTGRID
	bool even = false;
	unsigned int joinedplayers = 0;
	for ( unsigned int i=0; i < players.size(); i++ )
		if ( players[i]->isJoined() )
		{
			// lock car acceleration during countdown
			players[i]->getCar()->m_lockaccel = true;
			players[i]->getCar()->m_vehicle->applyEngineForce(0,0);
			players[i]->getCar()->m_vehicle->applyEngineForce(0,1);
			players[i]->getCar()->m_vehicle->applyEngineForce(0,2);
			players[i]->getCar()->m_vehicle->applyEngineForce(0,3);

			for ( int wheel=0; wheel < players[i]->getCar()->m_vehicle->getNumWheels(); wheel++ )
			{
				players[i]->getCar()->m_vehicle->interpolateInfo[wheel].currpolycount = 1;
				players[i]->getCar()->m_vehicle->interpolateInfo[wheel].prevpolycount = 1;
				players[i]->getCar()->m_vehicle->interpolateInfo[wheel].currnormal = btVector3(0,0,0);
				players[i]->getCar()->m_vehicle->interpolateInfo[wheel].prevnormal = btVector3(0,0,0);;
// 				(*players)[i]->getCar()->m_vehicle->interpolateInfo[wheel].clear();
			}

			players[i]->setTime(0);

			// move car to starting grid
			
			btTransform woffset;
			woffset.setIdentity();
			float leftoffset = 4.0f;
			if ( even ) leftoffset *= -1.0f;
			woffset.setOrigin( btVector3( leftoffset, 0.0f, 8.0f*((joinedplayers++)/2) ) );
			
			players[i]->getCar()->m_carChassis->setCenterOfMassTransform(map->startgrid*woffset);
			players[i]->getCar()->m_carChassis->setLinearVelocity(btVector3(0,0,0));
			players[i]->getCar()->m_carChassis->setAngularVelocity(btVector3(0,0,0));
			m_ownerWorld->getBroadphase()->getOverlappingPairCache()->cleanProxyFromPairs(players[i]->getCar()->m_carChassis->getBroadphaseHandle(),m_ownerWorld->getDispatcher());

			even = !even;
		}	
	
}

void Race::resetTimer( const Timer& t )
{
	time_start = t.sdl_now;
}

void Race::process( const Timer& t )
{
	time_elapsed = t.sdl_now - time_start;
// 	time_elapsed = 1;
// 	cerr << "duration: " << time_elapsed << endl;
// 	cerr << "processing race" << endl;
	if ( racestage == COUNTDOWN )
	{
		if ( time_elapsed > 1000*countdownTime )
		{
			resetTimer( t );
			racestage = RACE;

			// create ghostobject
			ghostObject = new btPairCachingGhostObject();
// 			ghostObject->setCollisionShape( new btBoxShape( btVector3( btVector3( map->hsectorsize-map->hwallwidth, (map->relativeY-map->wallwidth)/2, map->hwallwidth ) ) ) );
			ghostObject->setCollisionShape( new btBoxShape( btVector3( btVector3( map->finish_width, map->finish_height, 0.5f ) ) ) );
			ghostObject->setCollisionFlags( btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_NO_CONTACT_RESPONSE );
			ghostObject->setWorldTransform( map->finish );
// 			m_ownerWorld->addCollisionObject(ghostObject);

			// unlock car acceleration
			for ( unsigned int i=0; i < players.size(); i ++ )
				if ( players[i]->isJoined() )
					players[i]->getCar()->m_lockaccel = false;

			cerr << "race!" << endl;
// 			Logbuffer::Instance()->add(stringstream("race!"));
			newLogMessage("race!");
			newMessageAllClients("ms race!;");
		}
		else
		{
// 			if ( 1000*countdownTime - elapsed )
			unsigned short cur =  (1000*countdownTime - time_elapsed) / 1000 + 1;
			if ( cur != countdownCount )
			{
				countdownCount = cur;
				stringstream buf;
				buf << cur;
				cerr << buf.str() << endl;
// 				Logbuffer::Instance()->add(buf);
				newLogMessage(buf.str());
// 				cerr << buf.str() << endl;

				stringstream buf2;
				buf2 << "ms " << cur << ";";
				newMessageAllClients(buf2.str());
			}
		}
	}

	else if ( racestage == RACE )
	{
// 		cerr << "racing" << endl;
		checkFinish();
	}

	else if ( racestage == FINISHED )
	{
// 		cerr << "finished" << endl;
	}

// 	cerr << "race processed" << endl;
}

void Race::checkFinish()
{
		btBroadphasePairArray& pairArray = ghostObject->getOverlappingPairCache()->getOverlappingPairArray();
		int numPairs = pairArray.size();

		for ( int i=0; i < numPairs; i++ )
		{
// 			cerr << "checking numpairs" << endl;
			manifoldArray.clear();

			const btBroadphasePair& pair = pairArray[i];

			//unless we manually perform collision detection on this pair, the contacts are in the dynamics world paircache:
			btBroadphasePair* collisionPair = m_ownerWorld->getPairCache()->findPair(pair.m_pProxy0,pair.m_pProxy1);
			if (!collisionPair)
				continue;

			if (collisionPair->m_algorithm)
				collisionPair->m_algorithm->getAllContactManifolds(manifoldArray);

			for ( int j = 0; j < manifoldArray.size(); j++ )
			{
				btPersistentManifold* manifold = manifoldArray[j];
				
				btCollisionObject* object1 = static_cast<btCollisionObject*>(manifold->getBody0());
				btCollisionObject* object2 = static_cast<btCollisionObject*>(manifold->getBody1());

// 				if ( object1->getUserPointer() == c && object2->getUserPointer() == c )
// 					continue;

				for ( int p = 0; p < manifold->getNumContacts(); p++ )
				{
					const btManifoldPoint &pt = manifold->getContactPoint(p);
					if ( pt.getDistance() < 0.0f )
					{
// 			cerr << "collission" << endl;
						void* Collidingobject;
						if ( object1->getUserPointer() != 0 )
							Collidingobject = object1->getUserPointer();
						else if ( object2->getUserPointer() != 0 )
							Collidingobject = object2->getUserPointer();
						else 
							continue;

						Entity* e = static_cast<Entity*>(Collidingobject);
// 						cerr << e->type << endl;
						if ( e->type == CAR )
						{
							Car* c = static_cast<Car*>(e);
							boost::shared_ptr<Player> pl(players[ findPlayer(c->playerid) ]);
							if ( pl->getTime() == 0 )
							{
								pl->setTime(time_elapsed);

								stringstream logfinished;
								logfinished << "player " << pl->getId() << " finished with time " << time_elapsed;
								newLogMessage( logfinished.str() );
								cerr << logfinished.str() << endl;
								
								
	// 							stage = FINISHED;
	// 							c->touchingCritter = true;
	// 							c->touchedEntity = e;
								stringstream pfinished;
								pfinished << "pf " << pl->getId() << " " << time_elapsed << ";";
								newMessageAllClients( pfinished.str() );
// 								return;
							}
						}
					}
				}
			}
		}

}

int Race::findPlayer(unsigned int id)
{
	for ( unsigned int k=0; k < players.size(); k++ )
		if ( players[k]->getId() == id )
			return k;
	return -1;
}

void Race::newMessageAllClients(const string& msg)
{
	for ( unsigned int i=0; i < players.size(); i++ )
	{
		players[i]->pushOutgoingmessage(msg);
	}
}

void Race::newLogMessage(const string& msg)
{
	logbufferQueue->push_back(msg);
}


Race::~Race()
{
	if ( ghostObject )
	{
		m_ownerWorld->removeCollisionObject(ghostObject);
		delete ghostObject->getCollisionShape();
		delete ghostObject;
	}
}
